package com.kuizii.auth.config;


import com.kuizii.auth.JWTToken;
import com.kuizii.auth.shiro.*;
import org.apache.shiro.authc.AuthenticationListener;
import org.apache.shiro.authc.Authenticator;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionStorageEvaluator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.util.StringUtils;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;

import javax.servlet.Filter;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.Map;


@ConditionalOnProperty(name = "kuizii.shiro.enable", havingValue = "true")
@Configuration
@EnableConfigurationProperties(ShiroProperty.class)
@Order(Ordered.HIGHEST_PRECEDENCE)
public class ShiroConfig {


    public static final String ALGORITHM_NAME = "md5"; // 基础散列算法
    public static final int HASH_ITERATIONS = 1024; // 自定义散列次数

    @Autowired
    ShiroProperty shiroProperty;


    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {

        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        filterFactoryBean.setSecurityManager(securityManager);
        //认证失败，跳转到登录页面
        filterFactoryBean.setLoginUrl(shiroProperty.getLoginUrl());
        //认证成功，跳转到首页
        filterFactoryBean.setSuccessUrl(shiroProperty.getLoginSuccessUrl());
        //权限认证失败，跳转到无权限页面
        filterFactoryBean.setUnauthorizedUrl(shiroProperty.getUnauthUrl());


        Map<String, Filter> filterMap = new LinkedHashMap<String, Filter>();
        filterMap.put("kuiziiShiroFilter", kuiziiShiroFilter());
        filterFactoryBean.setFilters(filterMap);
        filterFactoryBean.setFilterChainDefinitionMap(shiroFilterChainDefinition().getFilterChainMap());

        return filterFactoryBean;
    }

    public ShiroFilterChainDefinition shiroFilterChainDefinition() {
        DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();

        String[] anonUrls = StringUtils.split(shiroProperty.getAnonUrl());
        if (anonUrls != null) {
            for (String anonUrl : anonUrls) {
                chainDefinition.addPathDefinition(anonUrl, "anon");
            }
        }

        chainDefinition.addPathDefinition("/**", "authc");

        return chainDefinition;
    }



    //开启shiro aop注解支持
//    @Bean
//    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor( SecurityManager securityManager) {
//        System.out.println("开启了Shiro注解支持");
//        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
//        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
//        return authorizationAttributeSourceAdvisor;
//    }

    /**
     * 基于密码验证算法
     *
     * @return
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {
        HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
        hashedCredentialsMatcher.setHashAlgorithmName(ALGORITHM_NAME); // 散列算法
        hashedCredentialsMatcher.setHashIterations(HASH_ITERATIONS); // 散列次数
        return hashedCredentialsMatcher;
    }

    /**
     * jwt  基于token 验证算法
     *
     * @return
     */
    public JWTCredentialsMatcher jwtCredentialsMatcher() {

        return new JWTCredentialsMatcher();
    }

    @Bean
    public KuiziiShiroFilter kuiziiShiroFilter() {

        return new KuiziiShiroFilter(shiroProperty.getLoginUrl());
    }

    /**
     * 禁用Session
     *
     * @return
     */
//    @Bean
    public SessionStorageEvaluator sessionStorageEvaluator() {
        DefaultWebSessionStorageEvaluator sessionStorageEvaluator = new DefaultWebSessionStorageEvaluator();
        sessionStorageEvaluator.setSessionStorageEnabled(false);
        return sessionStorageEvaluator;
    }

    /**
     * 该Realm满足通过db，验证帐号密码
     *
     * @return
     */
    @Bean
    public Realm shiroRealm() {
        KuiziiDbRealm shiroRealm = new KuiziiDbRealm();
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); // 原来在这里
        shiroRealm.setAuthenticationTokenClass(UsernamePasswordToken.class);
        return shiroRealm;
    }

    /**
     * 该Realm合适JWT形式，验证token
     *
     * @return
     */
    @Bean("jwtShiroRealm")
    public Realm jwtShiroRealm() {
        KuiziiStatelessRealm shiroRealm = new KuiziiStatelessRealm();
        shiroRealm.setCredentialsMatcher(jwtCredentialsMatcher()); // 原来在这里
        shiroRealm.setAuthenticationTokenClass(JWTToken.class);

        return shiroRealm;
    }


    /**
     * DefaultWebSecurityManager 默认基于cookie取得session
     * 不禁用session保持状态
     *
     * @return
     */
//    @Qualifier("securityManager")
//    @Bean("defaultWebSecurityManager")
//    @ConditionalOnProperty("kuizii.shiro.session.enable")
//    public DefaultWebSecurityManager defaultWebSecurityManager() {
//        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//        ArrayList<Realm> list = new ArrayList<>();
//        list.add(shiroRealm());
//        list.add(jwtShiroRealm());
//
//        securityManager.setRealms(list);
//        securityManager.setAuthenticator(authenticator());
//
//        ThreadContext.bind(securityManager);
    //SecurityUtils.setSecurityManager(securityManager);
//        return securityManager;
//    }

    /**
     * 禁用Session 用于jwt 无状态方式访问
     *
     * @return
     */
    @ConditionalOnMissingBean
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        ArrayList<Realm> list = new ArrayList<>();
        list.add(shiroRealm());
        list.add(jwtShiroRealm());

        securityManager.setRealms(list);
        securityManager.setSessionManager(new DefaultWebSessionManager());

        //禁用session
//        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
//        subjectDAO.setSessionStorageEvaluator(sessionStorageEvaluator());
//        securityManager.setSubjectDAO(subjectDAO);
//        securityManager.setAuthenticator(authenticator());
//        ThreadContext.bind(securityManager);

        return securityManager;
    }

    protected Authenticator authenticator() {
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        ArrayList<Realm> list = new ArrayList<>();
        list.add(shiroRealm());
        list.add(jwtShiroRealm());
        authenticator.setRealms(list);
        authenticator.setAuthenticationStrategy(new SingleOneFailStrategy());

        ArrayList<AuthenticationListener> listenerArrayList = new ArrayList<>();
        listenerArrayList.add(new KuiziiAuthenticationListener());
        authenticator.setAuthenticationListeners(listenerArrayList);
        return authenticator;
    }
}
